/**create by liuhua at 2019年7月2日 下午4:27:08**/
package com.star.truffle.core.util;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ListUtils {

  /**
   * 判断 subList 是否存在于 list，根据指定的field
   * @param list
   * @param subList
   * @param fieldNames
   * @return
   */
  public static <A, B> boolean containSubList(List<A> list, List<B> subList, String... fieldNames) {
    if(null == list || list.isEmpty() || null == subList || subList.isEmpty()) {
      return false;
    }
    
    List<String> tempb = subList.stream().map(b -> transform(b, fieldNames)).distinct().sorted().collect(Collectors.toList());
    long len = list.stream().map(a -> transform(a, fieldNames)).distinct().filter(item -> tempb.contains(item)).count();
    
    return len == tempb.size();
  }
  
  /**
   * 移除 list里面的subList
   * @param list
   * @param subList
   * @param fieldNames
   * @return
   */
  public static <A, B> List<A> removeSubList(List<A> list, List<B> subList, String... fieldNames) {
    if(null == list || list.isEmpty() || null == subList || subList.isEmpty()) {
      return list;
    }
    return list.stream().filter(a -> !contain(subList, a, fieldNames)).collect(Collectors.toList());
  }
  
  /**
   * 判断list里是否有重复记录，根据field
   * @param list
   * @param fieldNames
   * @return
   */
  public static <A> boolean hasDistinct(List<A> list, String... fieldNames) {
    List<A> temp = distinct(list, fieldNames);
    return temp.size() != list.size();
  }
  
  /**
   * 去重
   * @param <A>
   * @param list
   * @param fieldNames
   * @return
   */
  public static <A> List<A> distinct(List<A> list, String... fieldNames) {
    if (null == list || list.isEmpty()) {
      return new ArrayList<>();
    }
    List<A> temp = list;
    if (null == fieldNames || fieldNames.length == 0) {
      temp = list.stream().distinct().collect(Collectors.toList());
    } else {
      temp = list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(a -> transform(a, fieldNames)))), ArrayList::new));
    }
    return temp;
  }
  
  public static void main(String[] args) {
    List<Integer> list = new ArrayList<>(List.of(2,3,1,1,1,1,1,2,3,4,5,3,2,1,2312,312,312,312,312,3,1,2,1));
    System.out.println(list);
    Collections.reverse(list);
    System.out.println(list);
    list = distinct(list);
    System.out.println(list);
    
    Unix u1 = new Unix(5L, "你好1");
    Unix u2 = new Unix(2L, "你好6");
    Unix u3 = new Unix(1L, "你好5");
    Unix u4 = new Unix(3L, "你好4");
    Unix u5 = new Unix(1L, "你好3");
    Unix u6 = new Unix(2L, "你好2");
    Unix u7 = new Unix(1L, "你好1");
    List<Unix> unixs = List.of(u1, u2, u3, u4, u5, u6, u7);
    System.out.println(unixs);
    unixs = distinct(unixs, "name");
    System.out.println(unixs);
  }
  
  @Data
  @AllArgsConstructor
  @NoArgsConstructor
  public static class Unix{
    private Long unixId;
    private String name;
  }
  
  /**
   * list里的每一个元素是否在subList里最多只出现过一次
   * @param list
   * @param subList
   * @param fieldNames
   * @return
   */
  public static <A, B> boolean mostOnce(List<A> list, List<B> subList, String... fieldNames) {
    Map<String, Long> map = subList.stream().map(b -> transform(b, fieldNames)).collect(Collectors.groupingBy(item -> item, Collectors.counting()));
    long count = list.stream().map(a -> transform(a, fieldNames)).distinct().filter(item -> null != map.get(item) && map.get(item) > 1).count();
    return count == 0;
  }
  
  private static <A, B> boolean contain(List<B> list, A a, String... fieldNames) {
    String ta = transform(a, fieldNames);
    return list.stream().map(b -> {
      return transform(b, fieldNames);
    }).collect(Collectors.toList()).contains(ta);
  }
  
  public static <T> String transform(T a, String... fieldNames) {
    List<String> list = new ArrayList<>();
    if(fieldNames.length == 0) {
      list.add("this," + a.hashCode());
    }else {
      Arrays.stream(fieldNames).forEach(fieldName -> {
        list.add(fieldName + "," + getHashCode(a, fieldName));
      });
    }
    return list.stream().distinct().sorted().collect(Collectors.joining(","));
  }

  private static Integer getHashCode(Object obj, String fieldName) {
    Object value = getValue(obj, fieldName);
    if(null == value) {
      return 0;
    }
    return value.hashCode();
  }

  private static Object getValue(Object obj, String fieldName) {
    if(null == obj || StringUtils.isBlank(fieldName)) {
      return null;
    }
    if(obj instanceof Map) {
      Map<?, ?> map = (Map<?, ?>) obj;
      Object value = map.get(fieldName);
      return value;
    }else if (obj instanceof List) {
      return 0;
    }else if (obj instanceof Character || obj instanceof Short || obj instanceof Integer || 
        obj instanceof Long || obj instanceof Float || obj instanceof Double || obj instanceof Boolean) {
      return obj;
    }else {
      try {
        Method method = obj.getClass().getMethod("get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1));
        return method.invoke(obj);
      }catch(Exception e) {
        log.error(e.getMessage(), e);
        return obj;
      }
    }
  }
}
